#include <chrono>
#include <gtest/gtest.h>
#include <thread>

#include "sync/os_semaphore.h"

// 测试os_semaphore的构造函数和get_count方法
TEST(OsSemaphoreTest, ConstructorAndGetCount)
{
    // 默认构造函数，初始计数为0
    os_semaphore sem1;
    EXPECT_EQ(sem1.get_count(), 0);

    // 指定初始计数的构造函数
    os_semaphore sem2(5);
    EXPECT_EQ(sem2.get_count(), 5);

    // 测试负数初始化（虽然可能不常用，但需要验证行为）
    os_semaphore sem3(-1);
    EXPECT_EQ(sem3.get_count(), -1);
}

// 测试os_semaphore的signal和wait基本操作
TEST(OsSemaphoreTest, SignalAndWait)
{
    os_semaphore sem(0);

    // 初始计数为0
    EXPECT_EQ(sem.get_count(), 0);

    // 发送信号，计数应该增加
    sem.signal();
    EXPECT_EQ(sem.get_count(), 1);

    // 再发送一个信号
    sem.signal();
    EXPECT_EQ(sem.get_count(), 2);

    // 等待获取信号量，计数应该减少
    sem.wait();
    EXPECT_EQ(sem.get_count(), 1);

    // 再等待一次
    sem.wait();
    EXPECT_EQ(sem.get_count(), 0);
}

// 测试os_semaphore的signal(int n)批量发送信号
TEST(OsSemaphoreTest, SignalMultiple)
{
    os_semaphore sem(0);

    // 初始计数为0
    EXPECT_EQ(sem.get_count(), 0);

    // 批量发送5个信号量
    sem.signal(5);
    EXPECT_EQ(sem.get_count(), 5);

    // 再发送3个
    sem.signal(3);
    EXPECT_EQ(sem.get_count(), 8);

    // 消耗4个
    for (int i = 0; i < 4; ++i) {
        sem.wait();
    }
    EXPECT_EQ(sem.get_count(), 4);
}

// 测试os_semaphore的wait阻塞行为
TEST(OsSemaphoreTest, WaitBlocking)
{
    os_semaphore sem(0);

    // 记录开始时间
    auto start = std::chrono::steady_clock::now();

    // 创建一个线程来在一段时间后发送信号量
    std::thread sender([&sem]() {
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        sem.signal();
    });

    // 等待信号量（应该会被阻塞直到sender线程发送信号）
    sem.wait();

    // 记录结束时间
    auto end = std::chrono::steady_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

    // 确保确实等待了一段时间（至少30ms）
    EXPECT_GE(duration.count(), 30);

    sender.join();
}

// 测试os_semaphore的try_wait_for超时行为
TEST(OsSemaphoreTest, TryWaitForTimeout)
{
    os_semaphore sem(0);

    // 尝试等待10ms，应该超时返回false
    bool result = sem.try_wait_for(std::chrono::milliseconds(10));
    EXPECT_FALSE(result);
    EXPECT_EQ(sem.get_count(), 0);

    // 现在发送一个信号量
    sem.signal();

    // 再次尝试等待，应该立即成功返回true
    result = sem.try_wait_for(std::chrono::milliseconds(10));
    EXPECT_TRUE(result);
    EXPECT_EQ(sem.get_count(), 0);
}

// 测试os_semaphore的try_wait_for超时时间准确性
TEST(OsSemaphoreTest, TryWaitForTiming)
{
    os_semaphore sem(0);

    // 记录开始时间
    auto start = std::chrono::steady_clock::now();

    // 尝试等待50ms，应该超时
    bool result = sem.try_wait_for(std::chrono::milliseconds(50));

    // 记录结束时间
    auto end = std::chrono::steady_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

    // 确认超时
    EXPECT_FALSE(result);
    // 确认等待时间接近50ms（允许一定误差，5ms）
    EXPECT_GE(duration.count(), 45);
    EXPECT_LE(duration.count(), 55);
}

// 测试多线程环境下os_semaphore的正确性
TEST(OsSemaphoreTest, MultiThreadedUsage)
{
    os_semaphore sem(0);
    int shared_counter = 0;
    const int num_threads = 5;
    const int increments_per_thread = 10;

    // 创建多个线程，每个线程都会等待信号量然后增加计数器
    std::vector<std::thread> threads;

    for (int i = 0; i < num_threads; ++i) {
        threads.emplace_back([&sem, &shared_counter]() {
            for (int j = 0; j < increments_per_thread; ++j) {
                sem.wait();  // 等待信号量
                shared_counter++;
            }
        });
    }

    // 在主线程中发送信号量
    for (int i = 0; i < num_threads * increments_per_thread; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));  // 模拟一些工作
        sem.signal();                                               // 发送信号量
    }

    // 等待所有线程完成
    for (auto& t : threads) {
        t.join();
    }

    // 验证最终结果
    EXPECT_EQ(shared_counter, num_threads * increments_per_thread);
}

// 测试多个信号量实例的独立性
TEST(OsSemaphoreTest, MultipleInstancesIndependence)
{
    os_semaphore sem1(1);
    os_semaphore sem2(2);

    // 验证两个信号量初始计数正确
    EXPECT_EQ(sem1.get_count(), 1);
    EXPECT_EQ(sem2.get_count(), 2);

    // 操作第一个信号量
    sem1.wait();
    EXPECT_EQ(sem1.get_count(), 0);
    EXPECT_EQ(sem2.get_count(), 2);  // 第二个不受影响

    // 操作第二个信号量
    sem2.signal();
    EXPECT_EQ(sem1.get_count(), 0);  // 第一个不受影响
    EXPECT_EQ(sem2.get_count(), 3);
}